#ifndef __M_BINDING_H__
#define __M_BINDING_H__

#include "../mqcommon/logger.hpp"
#include "../mqcommon/helpper.hpp"
#include "../mqcommon/msg.pb.h"

#include <iostream>
#include <unordered_map>
#include <mutex>
#include <memory>

namespace mmq
{
    struct Binding
    {
    public:
        using ptr = std::shared_ptr<Binding>;
    public:
        Binding() {}
        Binding(const std::string &ename, const std::string &qname, const std::string &key)
        : _exchange_name(ename), _msgqueue_name(qname), _binding_key(key)
        {}
    public:
        std::string _exchange_name;
        std::string _msgqueue_name;
        std::string _binding_key;
    };

    // 队列与绑定信息是一一对应的(因为是给交换机绑定队列，所以一个交换机会有多个队列绑定信息)
    // 因此定义一个队列名与绑定信息的映射关系。这是为了方便通过队列名查找绑定信息。
    using MsgQueueBindingMap = std::unordered_map<std::string, Binding::ptr>;
    // 再定义一个交换机名与队列绑定信息的映射关系，这里面包含了所有绑定信息，以交换机名为单元区分
    using BindingMap = std::unordered_map<std::string, MsgQueueBindingMap>;

    // std::unordered_map<std::string, Binding::ptr>; 队列与绑定 
    // std::unordered_map<std::string, Binding::ptr>; 交换机与绑定
    // 采用上面两个结构，在删除交换机相关绑定信息时，不仅要删除交换机映射，还要删除对应队列中的映射，否则对象得不到释放
    class BindingMapper
    {
    public:
        BindingMapper(const std::string &dbfile) : _sql_helper(dbfile)
        {
            std::string pPath = FileHelper::ParentDirectory(dbfile);
            FileHelper::CreateDirectory(pPath);
            _sql_helper.Open();
            CreateTable();
        }
        void CreateTable()
        {
            std::stringstream sql;
            sql << "create table if not exists binding_table(";
            sql << "exchange_name varchar(32), ";
            sql << "msgqueue_name varchar(32), ";
            sql << "binding_key varchar(128));";
            assert(_sql_helper.Exec(sql.str(), nullptr, nullptr));
        }
        void RemoveTable()
        {
            std::string sql = "drop table if exists binding_table;";
            _sql_helper.Exec(sql, nullptr, nullptr);
        }
        bool Insert(Binding::ptr &bp)
        {
            std::stringstream sql;
            sql << "insert into binding_table values(";
            sql << "'" << bp->_exchange_name << "', ";
            sql << "'" << bp->_msgqueue_name << "', ";
            sql << "'" << bp->_binding_key << "');";
            return _sql_helper.Exec(sql.str(), nullptr, nullptr);
        }
        void Remove(const std::string &ename, const std::string &qname)
        {
            std::stringstream sql;
            sql << "delete from binding_table where ";
            sql << "exchange_name='" << ename << "' and ";
            sql << "msgqueue_name='" << qname << "';";
            _sql_helper.Exec(sql.str(), nullptr, nullptr);
        }
        void RemoveExchangeBindings(const std::string &ename)
        {
            std::stringstream sql;
            sql << "delete from binding_table where ";
            sql << "exchange_name='" << ename << "';";
            _sql_helper.Exec(sql.str(), nullptr, nullptr);
        }
        void RemoveMsgQueueBindings(const std::string &qname)
        {
            std::stringstream sql;
            sql << "delete from binding_table where ";
            sql << "msgqueue_name='" << qname << "';";
            _sql_helper.Exec(sql.str(), nullptr, nullptr);
        }
        BindingMap Recovery()
        {
            std::string sql = "select exchange_name, msgqueue_name, binding_key from binding_table;";
            BindingMap result;
            _sql_helper.Exec(sql, SelectCallback, &result);
            return result;
        }
    private:
        static int SelectCallback(void *arg, int colnum, char **row, char **fields)
        {
            BindingMap *result = (BindingMap*)arg;
            Binding::ptr bp = std::make_shared<Binding>(row[0], row[1], row[2]);
            // 为了防止 若交换机相关的绑定信息已经存在，不能直接创建队列映射，会覆盖历史数据
            // 因此先获取交换机对应的映射对象，再往里面添加数据
            MsgQueueBindingMap &mqbmap = (*result)[bp->_exchange_name];
            mqbmap.insert({bp->_msgqueue_name, bp});
            return 0;
        }
    private:
        SqliteHelper _sql_helper;
    };

    class BindingManager
    {
    public:
        using ptr = std::shared_ptr<BindingManager>;
        BindingManager(const std::string &dbfile) : _mapper(dbfile)
        {
            _bindings = _mapper.Recovery();
        }
        bool Bind(const std::string &ename, const std::string &qname, const std::string &key, bool durable)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _bindings.find(ename);
            if(it != _bindings.end() && it->second.find(qname) != it->second.end())
            {
                return true;
            }
            Binding::ptr bp = std::make_shared<Binding>(ename, qname, key);
            // 绑定信息是否进行持久化取决于----交换机和队列是否需要持久化
            // 为了解耦，这里利用外界传来的durable判断是否持久化
            // durable在进行联合处理时，绑定之前要先判断交换机和队列是否存在，在这个时候可以获取两者的_durable值
            if(durable)
            {
                bool ret = _mapper.Insert(bp);
                if(ret == false) return false;
            }
            MsgQueueBindingMap &mqbmap = _bindings[ename];
            mqbmap.insert({qname, bp});
            return true;
        }

        void UnBind(const std::string &ename, const std::string &qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto eit = _bindings.find(ename);
            if(eit == _bindings.end()) return;
            auto qit = eit->second.find(qname);
            if(qit == eit->second.end()) return;
            // 这里不管是否持久化，数据库直接删，反正不会有什么错误(效率虽然降低，但降低了耦合度)
            _mapper.Remove(ename, qname); 
            _bindings[ename].erase(qname);
        }
        
        void RemoveExchangeBindings(const std::string &ename)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.RemoveExchangeBindings(ename);
            _bindings.erase(ename);
        }

        void RemoveMsgQueueBindings(const std::string &qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.RemoveMsgQueueBindings(qname);
            // 遍历交换机，把每个交换机里的目标队列删除
            for(auto start = _bindings.begin(); start != _bindings.end(); ++start)
            {
                start->second.erase(qname);
            }
        }

        MsgQueueBindingMap GetExchangeBindings(const std::string &ename)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _bindings.find(ename);
            if(it == _bindings.end())
            {
                return MsgQueueBindingMap();
            }
            return it->second;
        }

        Binding::ptr GetBinding(const std::string &ename, const std::string &qname)
        {   
            std::unique_lock<std::mutex> lock(_mutex);
            auto eit = _bindings.find(ename);
            if(eit == _bindings.end()) return Binding::ptr();
            auto qit = eit->second.find(qname);
            if(qit == eit->second.end()) return Binding::ptr();
            return qit->second;
        }
        bool Exists(const std::string &ename, const std::string &qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto eit = _bindings.find(ename);
            if(eit == _bindings.end()) return false;
            auto qit = eit->second.find(qname);
            if(qit == eit->second.end()) return false;
            return true;
        }
        size_t Size()
        {
            size_t total_size = 0;
            std::unique_lock<std::mutex> lock(_mutex);
            // 遍历交换机，把每个交换机里的绑定队列数量
            for(auto start = _bindings.begin(); start != _bindings.end(); ++start)
            {
                total_size += start->second.size();
            }
            return total_size;
        }
        void Clear()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.RemoveTable();
            _bindings.clear();
        }
    private:
        std::mutex _mutex;
        BindingMapper _mapper;
        BindingMap _bindings;   // 所有的绑定信息
    };

}
#endif